<?php

namespace app\announce\controller;

use app\common\Benc;
use think\Controller;

class TrackerController extends Controller
{
	public function Benc()
	{
		return new Benc();
	}

	public function index($uuid)
	{
//		$this->banBrowser();//禁止浏览器访问
		$info = input("param.");
		if (empty($info['uuid']) || strlen($info['uuid']) != 21) {
			$this->Benc()->err("无效的uuid");
		} elseif (empty($info['info_hash']) || strlen($info['info_hash']) != 20) {
			$this->Benc()->err("无效的info_hash");
		} elseif (empty($info['peer_id']) || strlen($info['peer_id']) != 20) {
			if (preg_match("/^-(TR|AZ|DE|UM|UT|lt|qB)([0-9]{4})-/", $info['peer_id'])) {
				$this->Benc()->err("禁止的客户端");
			} else {
				$this->Benc()->err("无效的peer_id");
			}
		}

		$field = [
			"id", "maintitle", "olcate", "slcate", "uploader", "addtime", "sticklevel", "sticktime", "display", "status", "hr", "discount", "discounttime", "torrentsize", "type", "seeder", "leecher"
		];
		$t_info = db("movie")->field($field)->where("bininfohash", $info['info_hash'])->union(function ($query) use ($info, $field) {
			$query->name("series")->field($field)->where("bininfohash", $info['info_hash']);
		})->select();
		if ($t_info) {
			foreach ($t_info as $k) {
				$t_info = $k;
			}
			$db = getTorrentDB($t_info['type']);
		} else {
			$this->Benc()->err("不存在的info_hash");
		}
		$u_info = db("users")->where("uuid", $uuid)->find();
		if (!$u_info) {
			$this->Benc()->err("不存在的uuid");
		}

		$ip = ip();
		$interval = 30 * 60;
		$min_interval = 60;
		$info['numwant'] = $info['numwant'] > 50 ? 50 : $info['numwant'];
		$seeder = $info['left'] == 0 ? 1 : 0;//如果剩余量=0，则该用户为做种者；1=做种者，0=下载者
		$peer_where = [
			"torrentid" => ["=", $t_info['id']],
			"type" => ["=", $t_info['type']]
		];
		if ($seeder == 0) {
			$check_where = array_merge($peer_where, ["seeder" => ["=", "1"]]);
			if (db("peers")->where($check_where)->count("torrentid") > 1) {
				$peer_where = array_merge($peer_where, ["seeder" => ["=", "0"], "connectable" => ["=", "0"]]);
			}
		}
		$peerlist = db("peers")->where($peer_where)->order("RAND()")->limit($info['numwant'])->select();

		$ipv6 = input("?ipv6");
		if ($ipv6) {
			$info['ipv6'] = input("ipv6");
		} else {
			$info['ipv6'] = "";
		}

		if (preg_match("/^fe80:/i", $info['ipv6'])) {//如果IPv6地址匹配到"fe80:"，则IPv6地址为空
			$info['ipv6'] = "";
		} elseif ($ip == $info['ipv6']) {
			$info['ipv6'] = "";
		}

		$resp = "d" . $this->Benc()->benc_str("interval") . "i" . $interval . "e" . $this->Benc()->benc_str("min interval") . "i" . $min_interval . "e" . $this->Benc()->benc_str("complete") . "i" . $t_info['seeder'] . "e" . $this->Benc()->benc_str("incomplete") . "i" . $t_info['leecher'] . "e" . $this->Benc()->benc_str("peers");
		$peer_list = "";
		unset($self);
		foreach ($peerlist as $k) {
			if ($k["peerid"] === $info['peer_id']) {
				$self = TRUE;
				continue;
			}
			if ($info['compact'] == "1") {
				$longip = ip2long($ip);
				if ($longip) {
					$peer_list .= pack("Nn", sprintf("%d", $longip), $k['port']);
				}
			} elseif ($info['no_peer_id'] == "1") {
				$peer_list .=
					"d" .
					$this->Benc()->benc_str("ip") . $this->Benc()->benc_str($k['ip']) .
					$this->Benc()->benc_str("port") . "i" . $k['port'] . "e" .
					"e";
				if (!empty($info['ipv6'])) {
					$peer_list .=
						"d" .
						$this->Benc()->benc_str("ip") . $this->Benc()->benc_str($k['ipv6']) .
						$this->Benc()->benc_str("port") . "i" . $k['port'] . "e" .
						"e";
				}
			} elseif ($info['no_peer_id'] == "0") {
				$peer_list .=
					"d" .
					$this->Benc()->benc_str("ip") . $this->Benc()->benc_str($k['ip']) .
					$this->Benc()->benc_str("peer id") . $this->Benc()->benc_str($k['peer_id']) .
					$this->Benc()->benc_str("port") . "i" . $k['port'] . "e" .
					"e";
				if (!empty($info['ipv6'])) {
					$peer_list .=
						"d" .
						$this->Benc()->benc_str("ip") . $this->Benc()->benc_str($k['ipv6']) .
						$this->Benc()->benc_str("peer id") . $this->Benc()->benc_str($k['peer_id']) .
						$this->Benc()->benc_str("port") . "i" . $k['port'] . "e" .
						"e";
				}
			}
		}
		if ($info['compact'] == 1) {
			$resp .= $this->Benc()->benc_str($peer_list);
		} else {
			$resp .= "l" . $peer_list . "e";
		}
		$resp .= "e";

		//动作 -- 开始
		$where = [
			"torrentid" => ["=", $t_info['id']],
			"type" => ["=", $t_info['type']],
			"userid" => ["=", $u_info['uid']]
		];

		if (!isset($self)) {
			$res = db("peers")->where($where)->count("torrentid");
			if ($res >= 1 && $seeder == 0) {
				$this->Benc()->err("已经在其他IP地址下载");
			}
			if ($res >= 3 && $seeder == 1) {
				$this->Benc()->err("最多在3个IP地址做种");
			}
		}

		$check_connectable = function ($ip, $port) {
			if (ip2long($ip)) {//非IPv6
				$sockres = @fsockopen("tcp://$ip", $port, $errno, $errstr, 5);
			} else {
				$sockres = @fsockopen("tcp://[$ip]", $port, $errno, $errstr, 5);
			}
			//return $connectable 0=可连接，1=不可连接
			if ($errno == 0 && $errstr == "") {
				$connectable = "0";
				@fclose($sockres);
			} else {
				$connectable = "1";
			}

			return $connectable;
		};

		$event = input("?event");
		if ($event) {
			$info['event'] = input("event");
		} else {
			$info['event'] = "";
		}
		$uploaded = uploaded($info['uploaded'], $t_info['discount']);
		$downloaded = downloaded($info['downloaded'], $t_info['discount']);
		if (isset($self) && $info['event'] == "stopped") {
			try {
				db()->transaction(function () use ($where, $info, $uploaded, $downloaded) {
					$update = [
						"lefted" => $info['left'],
						"lasttime" => nowtime()
					];
					db("peers")->where($where)->delete();
					db("snatched")->where($where)->inc("uploaded", $uploaded)->inc("downloaded", $downloaded)->update($update);
				});
			} catch (\Exception $exception) {
				$this->Benc()->err("Error Code: 0x00000001");
			}
		} elseif (isset($self) && $info['event'] == "completed") {
			try {
				db()->transaction(function () use ($where, $seeder, $info, $ip, $check_connectable, $db, $t_info, $u_info, $uploaded, $downloaded) {
					$update = [
						"ip" => $ip,
						"ipv6" => $info['ipv6'],
						"port" => $info['port'],
						"lefted" => $info['left'],
						"lasttime" => nowtime(),
						"finishtime" => nowtime(),
						"connectable" => $check_connectable($ip, $info['port']),
						"seeder" => $seeder
					];
					$insert = array_merge($update, [
						"peerid" => $info['peer_id'],
						"torrentid" => $t_info['id'],
						"userid" => $u_info['uid'],
						"type" => $t_info['type'],
						"starttime" => nowtime()
					]);
					db("peers")->where($where)->delete();
					db("peers")->insert($insert);
					db("snatched")->where($where)->inc("uploaded", $uploaded)->inc("downloaded", $downloaded)->update($update);
					db($db)->where("id", $t_info['id'])->inc("completed")->update();
					db("users")->where("uid", $u_info['uid'])->inc("complete")->update();
				});
			} catch (\Exception $exception) {
				$this->Benc()->err("Error Code: 0x00000002");
			}
		} else {
			try {
				db()->transaction(function () use ($where, $seeder, $info, $ip, $check_connectable, $t_info, $u_info, $uploaded, $downloaded) {
					$insert = [
						"ip" => $ip,
						"ipv6" => $info['ipv6'],
						"port" => $info['port'],
						"uploaded" => $info['uploaded'],
						"downloaded" => $info['downloaded'],
						"lefted" => $info['left'],
						"starttime" => nowtime(),
						"lasttime" => nowtime(),
						"connectable" => $check_connectable($ip, $info['port']),
						"seeder" => $seeder,
						"peerid" => $info['peer_id'],
						"torrentid" => $t_info['id'],
						"userid" => $u_info['uid'],
						"type" => $t_info['type']
					];
					if (empty(db("snatched")->where($where)->count("id"))) {
						db("snatched")->insert($insert);
					} else {
						$update = [
							"ip" => $ip,
							"ipv6" => $info['ipv6'],
							"port" => $info['port'],
							"lefted" => $info['left'],
							"lasttime" => nowtime(),
							"connectable" => $check_connectable($ip, $info['port']),
							"seeder" => $seeder,
							"peerid" => $info['peer_id']
						];
						db("snatched")->where($where)->inc("uploaded", $uploaded)->inc("downloaded", $downloaded)->update($update);
					}
					$peers_where = array_merge($where, ["ip" => $ip, "port" => $info['port']]);
					if (empty(db("peers")->where($peers_where)->count("torrentid"))) {
						db("peers")->insert($insert);
					} else {
						db("peers")->where($peers_where)->delete();
						db("peers")->insert($insert);
					}
				});
			} catch (\Exception $exception) {
				$this->Benc()->err("Error Code: 0x00000003");
			}
		}
		//更新种子做种数，下载数
		$seeders_where = [
			"torrentid" => ["=", $t_info['id']],
			"type" => ["=", $t_info['type']],
			"seeder" => ["=", "1"]
		];
		$seeders = db("peers")->where($seeders_where)->count("torrentid");
		$leecher_where = [
			"torrentid" => ["=", $t_info['id']],
			"type" => ["=", $t_info['type']],
			"seeder" => ["=", "0"]
		];
		$leechers = db("peers")->where($leecher_where)->count("torrentid");
		try {
			db()->transaction(function () use ($db, $seeders, $leechers, $t_info, $info, $where) {
				db($db)->where("id", $t_info['id'])->update(["seeder" => $seeders, "leecher" => $leechers]);
				$snatched = db("snatched")->where($where)->field(["starttime", "finishtime", "lasttime"])->find();
				$seedtime = 0;
				if (!strtotime($snatched['finishtime'])) {
					//未完成下载
					$leechtime = strtotime($snatched['lasttime']) - strtotime($snatched['starttime']);
				} else {
					//完成下载
					$seedtime = strtotime($snatched['lasttime']) - strtotime($snatched['finishtime']);
					$leechtime = strtotime($snatched['finishtime']) - strtotime($snatched['starttime']);
				}
				db("snatched")->where($where)->update([
					"seedtime" => $seedtime,
					"leechtime" => $leechtime
				]);
			});
		} catch (\Exception $exception) {
			$this->Benc()->err("Error Code: 0x00000004");
		}
		//动作 -- 结束

		$this->Benc()->benc_resp_raw($resp);

//		xxx.xxx.xxx/announce/$uuid
	}

	public function banBrowser()
	{
		$ua = $_SERVER['HTTP_USER_AGENT'];
		if (!preg_match("/^Transmission|Azureus|Deluge|uTorrentMac|uTorrent|rtorrent|qBittorrent/", $ua)) {
			$this->Benc()->err("禁止使用浏览器访问Tracker");
		}
	}

}
